package org.budo.sso;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.budo.sso.config.GiteeSsoConfig;
import org.budo.sso.config.OschinaSsoConfig;
import org.budo.sso.config.TencentSsoConfig;
import org.budo.sso.config.WeixinSsoConfig;
import org.budo.support.java.net.util.UrlEncodeUtil;
import org.budo.support.lang.util.StringUtil;
import org.budo.support.mvcs.Mvcs;
import org.budo.support.servlet.util.ResponseUtil;
import org.budo.support.spring.util.SpringUtil;
import org.budo.tencent.Tencent;
import org.budo.tencent.api.TencentOauthApi;
import org.budo.tencent.response.TokenResponse;
import org.budo.tencent.response.UnionIdResponse;
import org.budo.tencent.response.UserInfoResponse;
import org.budo.weixin.api.WeixinApi;
import org.budo.weixin.api.WeixinApiImpl;
import org.budo.weixin.api.response.SnsTokenResponse;
import org.budo.weixin.api.response.SnsUserInfoResponse;

import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

/**
 * @author lmw
 */
@Slf4j
@Getter
@Setter
public abstract class BudoSsoFilter implements Filter {
    private String passUrl;

    protected abstract void redirect(String platform, String appKey, String openId, String nickname);

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        log.info("#21 init, this=" + this + ", filterConfig=" + filterConfig);
    }

    @Override
    public void destroy() {
        log.info("#31 destroy, this=" + this);
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        String requestURI = Mvcs.getRequestURI(request);
        log.info("#26 doFilter, request=" + requestURI);

        String serverName = Mvcs.getServerName();
        if (StringUtil.endsWith(requestURI, "/sso/authorize")) {
            String referer = request.getParameter("redirect_uri");
            if (StringUtil.isNullOrEmpty(referer)) {
                referer = Mvcs.getReferer();
            }

            Mvcs.setSessionAttribute("sso_referer_0", referer);
            Mvcs.setSessionAttribute("sso_referer_server_name", serverName);

            log.info("#64 referer=" + referer + ", serverName=" + serverName);

            ResponseUtil.sendRedirect(this.getPassUrl() + "/sso/authorize_1?" + Mvcs.getQueryString() + "&redirect_uri=" + UrlEncodeUtil.encode(referer));
            return;
        }

        if (StringUtil.endsWith(requestURI, "/sso/authorize_1")) {
            String referer = request.getParameter("redirect_uri");
            if (StringUtil.isNullOrEmpty(referer)) {
                referer = Mvcs.getReferer();
            }

            Mvcs.setSessionAttribute("sso_referer", referer);
            log.info("#51 sso_referer=" + referer + ", serverName=" + serverName);

            this.authorize_1(request, response);
            return;
        }

        if (StringUtil.endsWith(requestURI, "sso/tencent_redirect")) {
            this.tencent_redirect(request, response);
            return;
        }

        if (StringUtil.endsWith(requestURI, "sso/tencent_redirect_1")) {
            this.tencent_redirect_1(request, response);
            return;
        }

        if (StringUtil.endsWith(requestURI, "sso/weixin_redirect")) {
            this.weixin_redirect(request, response);
            return;
        }

        if (StringUtil.endsWith(requestURI, "sso/weixin_redirect_1")) {
            this.weixin_redirect_1(request, response);
            return;
        }

        chain.doFilter(request, response);
    }

    private void authorize_1(ServletRequest request, ServletResponse response) {
        log.info("#107 authorize_1, serverName=" + request.getServerName() + ", parameters=" + Mvcs.getRequestParameterMap(request));

        String platform = request.getParameter("platform");
        if ("Tencent".equals(platform)) {
            TencentSsoConfig tencentSsoConfig = SpringUtil.getBean(TencentSsoConfig.class);
            String url = "https://graph.qq.com/oauth2.0/authorize" //
                    + "?response_type=code" //
                    + "&client_id=" + tencentSsoConfig.getAppKey() //
                    + "&redirect_uri=http://anyan.co/sso/tencent_redirect";
            ResponseUtil.sendRedirect(response, url);
            return;
        }

        if ("Oschina".equals(platform)) {
            OschinaSsoConfig oschinaSsoConfig = SpringUtil.getBean(OschinaSsoConfig.class);
            String url = "https://www.oschina.net/action/oauth2/authorize" //
                    + "?client_id=" + oschinaSsoConfig.getAppKey() //
                    + "&redirect_uri=http://anyan.co/sso/oschina_redirect" //
                    + "&response_type=code";
            ResponseUtil.sendRedirect(response, url);
            return;
        }

        if ("Gitee".equals(platform)) {
            GiteeSsoConfig giteeSsoConfig = SpringUtil.getBean(GiteeSsoConfig.class);
            String url = "https://gitee.com/oauth/authorize" //
                    + "?client_id=" + giteeSsoConfig.getAppKey() //
                    + "&redirect_uri=http://anyan.co/sso/gitee_redirect" //
                    + "&response_type=code";
            ResponseUtil.sendRedirect(response, url);
            return;
        }

        if ("Weixin".equals(platform)) {
            WeixinSsoConfig weixinSsoConfig = SpringUtil.getBean(WeixinSsoConfig.class);
            String url = "https://open.weixin.qq.com/connect/oauth2/authorize" //
                    + "?appid=" + weixinSsoConfig.getAppKey() //
                    + "&redirect_uri=http://anyan.co/sso/weixin_redirect" //
                    + "&response_type=code" //
                    + "&scope=snsapi_userinfo";
            ResponseUtil.sendRedirect(response, url);
            return;
        }

        log.error("#70 request=" + request);
        ResponseUtil.writeToResponse("platform=" + platform);
    }

    private void weixin_redirect(ServletRequest request, ServletResponse response) {
        String referer = (String) Mvcs.getSessionAttribute("sso_referer");
        log.info("#82 sso_referer=" + referer + ", serverName=" + Mvcs.getServerName());

        String schemaDomain = Mvcs.schemaDomain(referer);
        ResponseUtil.sendRedirect(schemaDomain + "/sso/weixin_redirect_1?" + Mvcs.getQueryString());
    }

    private void weixin_redirect_1(ServletRequest request, ServletResponse response) {
        log.info("#164 request=" + request + ", serverName=" + Mvcs.getServerName());

        String code = request.getParameter("code");

        WeixinSsoConfig weixinSsoConfig = SpringUtil.getBean(WeixinSsoConfig.class);
        String appKey = weixinSsoConfig.getAppKey();
        String appSecret = weixinSsoConfig.getAppSecret();

        WeixinApi weixinApi = new WeixinApiImpl();
        SnsTokenResponse snsToken = weixinApi.getSnsToken(appKey, appSecret, code);

        String access_token = snsToken.getAccess_token();
        String openId = snsToken.getOpenid();
        if (StringUtil.isEmpty(openId)) {
            log.error("#163 code=" + code + ", request=" + request);
            ResponseUtil.writeToResponse(response, "token=" + snsToken);
            return;
        }

        weixinApi.getWeixinToken(appKey, appSecret);
        SnsUserInfoResponse userInfoResponse = weixinApi.getSnsUserInfo(access_token, openId);

        this.redirect("Weixin", appKey, openId, userInfoResponse.getNickname());
    }

    private void tencent_redirect(ServletRequest request, ServletResponse response) {
        String referer = (String) Mvcs.getSessionAttribute("sso_referer");
        log.info("#82 sso_referer=" + referer + ", serverName=" + Mvcs.getServerName());

        String schemaDomain = Mvcs.schemaDomain(referer);
        ResponseUtil.sendRedirect(schemaDomain + "/sso/tencent_redirect_1?" + Mvcs.getQueryString());
        return;
    }

    private void tencent_redirect_1(ServletRequest request, ServletResponse response) {
        String code = request.getParameter("code");

        TencentSsoConfig tencentSsoConfig = SpringUtil.getBean(TencentSsoConfig.class);
        String appKey = tencentSsoConfig.getAppKey();

        TencentOauthApi oauthApi = Tencent.oauthApi(appKey, tencentSsoConfig.getAppSecret(), tencentSsoConfig.getRedirectUri());
        TokenResponse token = oauthApi.token(code);

        String accessToken = token.getAccessToken();
        if (StringUtil.isEmpty(accessToken)) {
            log.error("#58 code=" + code + ", request=" + request);
            ResponseUtil.writeToResponse(response, "token=" + token);
            return;
        }

        UnionIdResponse unionIdResponse = Tencent.oauthApi().unionId(accessToken);
        String unionId = unionIdResponse.getUnionId();

        UserInfoResponse userInfoResponse = Tencent.userApi(appKey).getUserInfo(accessToken, unionIdResponse.getOpenId());
        String nickname = userInfoResponse.getNickname();

        this.redirect("Tencent", appKey, unionId, nickname);
    }
}